/**
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/base/builtins_base.h"
#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/js_bigint.h"
#include "plugins/ecmascript/runtime/js_primitive_ref.h"
#include "plugins/ecmascript/tests/runtime/common/test_helper.h"

// NOLINTBEGIN(readability-magic-numbers)

// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript;
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace ark::ecmascript::builtins;

namespace ark::test {
using BigInt = ecmascript::BigInt;
class BuiltinsBigIntTest : public testing::Test {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "SetUpTestCase";
    }

    static void TearDownTestCase()
    {
        GTEST_LOG_(INFO) << "TearDownCase";
    }

    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_);
    }

    void TearDown() override
    {
        TestHelper::DestroyEcmaVMWithScope(instance_, scope_);
    }

protected:
    // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
    JSThread *thread_ {nullptr};

private:
    ecmascript::JSHandle<ecmascript::JSFunction> methodFunction_;
    PandaVM *instance_ {nullptr};
    EcmaHandleScope *scope_ {nullptr};
};

// new BigInt(123)
TEST_F(BuiltinsBigIntTest, BigIntConstructor1)
{
    JSHandle<JSTaggedValue> numericValue(thread_, JSTaggedValue(123));
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::BigIntConstructor(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
}

// new BigInt("456")
TEST_F(BuiltinsBigIntTest, BigIntConstructor2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("456");

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::BigIntConstructor(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
}

// AsIntN(64, (2 ^ 63 - 1))
TEST_F(BuiltinsBigIntTest, AsIntN1)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("9223372036854775807");
    int bit = 64;  // 64-bit

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(bit)));
    ecmaRuntimeCallInfo->SetCallArg(1, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::AsIntN(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
    JSHandle<BigInt> bigIntHandle(thread_, result);
    JSHandle<EcmaString> resultStr = BigInt::ToString(thread_, bigIntHandle);
    JSHandle<EcmaString> str = factory->NewFromCanBeCompressString("9223372036854775807");
    EXPECT_EQ(resultStr->Compare(*str), 0);
}

// AsIntN(64, (2 ^ 63))
TEST_F(BuiltinsBigIntTest, AsIntN2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("9223372036854775808");
    int bit = 64;  // 64-bit

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(bit)));
    ecmaRuntimeCallInfo->SetCallArg(1, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::AsIntN(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
    JSHandle<BigInt> bigIntHandle(thread_, result);
    JSHandle<EcmaString> resultStr = BigInt::ToString(thread_, bigIntHandle);
    JSHandle<EcmaString> str = factory->NewFromCanBeCompressString("-9223372036854775808");
    EXPECT_EQ(resultStr->Compare(*str), 0);
}

// AsUintN(64, (2 ^ 64 - 1))
TEST_F(BuiltinsBigIntTest, AsUintN1)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("18446744073709551615");
    int bit = 64;  // 64-bit

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(bit)));
    ecmaRuntimeCallInfo->SetCallArg(1, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::AsUintN(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
    JSHandle<BigInt> bigIntHandle(thread_, result);
    JSHandle<EcmaString> resultStr = BigInt::ToString(thread_, bigIntHandle);
    JSHandle<EcmaString> str = factory->NewFromCanBeCompressString("18446744073709551615");
    EXPECT_EQ(resultStr->Compare(*str), 0);
}

// AsUintN(64, (2 ^ 64))
TEST_F(BuiltinsBigIntTest, AsUintN2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("18446744073709551616");
    int bit = 64;  // 64-bit

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(bit)));
    ecmaRuntimeCallInfo->SetCallArg(1, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = big_int::AsUintN(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result.IsBigInt());
    JSHandle<BigInt> bigIntHandle(thread_, result);
    JSHandle<EcmaString> resultStr = BigInt::ToString(thread_, bigIntHandle);
    JSHandle<EcmaString> str = factory->NewFromCanBeCompressString("0");
    EXPECT_EQ(resultStr->Compare(*str), 0);
}

// using locale
TEST_F(BuiltinsBigIntTest, ToLocaleString1)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("123456789123456789");

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    JSHandle<EcmaString> locale = factory->NewFromCanBeCompressString("de-DE");

    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, locale.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(1, JSTaggedValue::Undefined());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToLocaleString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    JSHandle<EcmaString> resultStr = factory->NewFromCanBeCompressString("123.456.789.123.456.789");
    EXPECT_EQ(ecmaStrHandle->Compare(*resultStr), 0);
}

// using locale and options
TEST_F(BuiltinsBigIntTest, ToLocaleString2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
    JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("123456789123456789");
    JSHandle<JSTaggedValue> formatStyle = thread_->GlobalConstants()->GetHandledStyleString();
    JSHandle<JSTaggedValue> styleKey(factory->NewFromCanBeCompressString("currency"));
    JSHandle<JSTaggedValue> styleValue(factory->NewFromCanBeCompressString("EUR"));

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    JSHandle<EcmaString> locale = factory->NewFromCanBeCompressString("de-DE");
    JSObject::SetProperty(thread_, optionsObj, formatStyle, styleKey);
    JSObject::SetProperty(thread_, optionsObj, styleKey, styleValue);

    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, locale.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(1, optionsObj.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToLocaleString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    EXPECT_STREQ("123.456.789.123.456.789,00 €", PandaString(ecmaStrHandle->GetCString().get()).c_str());
}

// 17.ToStirng()
TEST_F(BuiltinsBigIntTest, ToString1)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("17");

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue::Undefined());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    EXPECT_STREQ("17", PandaString(ecmaStrHandle->GetCString().get()).c_str());
}

// -0.ToStirng()
TEST_F(BuiltinsBigIntTest, ToString2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("-0");

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, JSTaggedValue::Undefined());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    EXPECT_STREQ("0", PandaString(ecmaStrHandle->GetCString().get()).c_str());
}

// -10.ToStirng(2)
TEST_F(BuiltinsBigIntTest, ToString3)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("-10");

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    JSHandle<JSTaggedValue> radix(thread_, JSTaggedValue(2));
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, radix.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    EXPECT_STREQ("-1010", PandaString(ecmaStrHandle->GetCString().get()).c_str());
}

// 254.ToStirng(16)
TEST_F(BuiltinsBigIntTest, ToString4)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("254");

    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo1->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo1.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo1.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    JSHandle<JSTaggedValue> radix(thread_, JSTaggedValue(16));
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());
    ecmaRuntimeCallInfo2->SetCallArg(0, radix.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ToString(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_TRUE(result2.IsString());
    JSHandle<EcmaString> ecmaStrHandle(thread_, result2);
    EXPECT_STREQ("fe", PandaString(ecmaStrHandle->GetCString().get()).c_str());
}

// BigInt.ValueOf
TEST_F(BuiltinsBigIntTest, ValueOf1)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("-65536");

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(bigIntHandle.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ValueOf(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_EQ(BigInt::SameValue(thread_, result1, result2), true);
}

// Object.ValueOf
TEST_F(BuiltinsBigIntTest, ValueOf2)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> numericValue = factory->NewFromCanBeCompressString("65535");

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, numericValue.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo.get());
    JSTaggedValue result1 = big_int::BigIntConstructor(ecmaRuntimeCallInfo.get());
    TestHelper::TearDownFrame(thread_, prev);

    JSHandle<BigInt> bigIntHandle(thread_, result1);
    JSHandle<JSTaggedValue> bigIntObj(bigIntHandle);

    JSHandle<JSPrimitiveRef> jsPrimitiveRef = factory->NewJSPrimitiveRef(PrimitiveType::PRIMITIVE_BIGINT, bigIntObj);
    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 4);
    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo2->SetThis(jsPrimitiveRef.GetTaggedValue());

    prev = TestHelper::SetupFrame(thread_, ecmaRuntimeCallInfo2.get());
    JSTaggedValue result2 = big_int::proto::ValueOf(ecmaRuntimeCallInfo2.get());
    TestHelper::TearDownFrame(thread_, prev);

    EXPECT_EQ(BigInt::SameValue(thread_, bigIntHandle.GetTaggedValue(), result2), true);
}

// testcases of NumberToBigint()
TEST_F(BuiltinsBigIntTest, NumberToBigint)
{
    JSHandle<JSTaggedValue> number(thread_, JSTaggedValue::Undefined());
    JSHandle<JSTaggedValue> bigint(thread_, JSTaggedValue::Undefined());

    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(base::MAX_VALUE));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, number));
    ASSERT_TRUE(bigint->IsBigInt());
    bool compareRes = JSTaggedValue::Equal(thread_, number, bigint);
    ASSERT_TRUE(compareRes);

    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-base::MAX_VALUE));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, number));
    ASSERT_TRUE(bigint->IsBigInt());
    compareRes = JSTaggedValue::Equal(thread_, number, bigint);
    ASSERT_TRUE(JSHandle<BigInt>::Cast(bigint)->GetSign());
    ASSERT_TRUE(compareRes);

    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-0xffffffff));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, number));
    ASSERT_TRUE(bigint->IsBigInt());
    compareRes = JSTaggedValue::Equal(thread_, number, bigint);
    ASSERT_TRUE(compareRes);

    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(0));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, number));
    ASSERT_TRUE(bigint->IsBigInt());
    compareRes = JSTaggedValue::Equal(thread_, number, bigint);
    ASSERT_TRUE(compareRes);
}

// testcases of BigintToNumber()
TEST_F(BuiltinsBigIntTest, BigintToNumber)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> bigint(thread_, JSTaggedValue::Undefined());
    JSTaggedNumber number(0);

    JSHandle<JSTaggedValue> parma(factory->NewFromCanBeCompressString("0xffff"));
    bigint = JSHandle<JSTaggedValue>(thread_, JSTaggedValue::ToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(number.GetNumber(), static_cast<double>(0xffff));

    parma = JSHandle<JSTaggedValue>(
        factory->NewFromCanBeCompressString("0xfffffffffffff8000000000000000000000000000000000000000000000000000"
                                            "0000000000000000000000000000000000000000000000000000000000000000000"
                                            "0000000000000000000000000000000000000000000000000000000000000000000"
                                            "000000000000000000000000000000000000000000000000000000000"));
    bigint = JSHandle<JSTaggedValue>(thread_, JSTaggedValue::ToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(number.GetNumber(), base::MAX_VALUE);

    parma = JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False());
    bigint = JSHandle<JSTaggedValue>(thread_, JSTaggedValue::ToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    ASSERT_TRUE(JSHandle<BigInt>::Cast(bigint)->IsZero());
    number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(number.GetNumber(), 0.0);

    parma = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(base::MAX_VALUE));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(number.GetNumber(), base::MAX_VALUE);

    parma = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(-base::MAX_VALUE));
    bigint = JSHandle<JSTaggedValue>(thread_, BigInt::NumberToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(number.GetNumber(), -base::MAX_VALUE);

    /*
        // clang-14 with ASAN release - Illegal instruction (core dumped)
        parma = JSHandle<JSTaggedValue>(thread, JSTaggedValue(-0xffffffff));
        bigint = JSHandle<JSTaggedValue>(thread, BigInt::NumberToBigInt(thread, parma));
        ASSERT_TRUE(bigint->IsBigInt());
        number = BigInt::BigIntToNumber(JSHandle<BigInt>::Cast(bigint));
        ASSERT_EQ(number.GetNumber(), -0xffffffff);
    */
}

// testcases of StringToBigInt(EcmaString)
TEST_F(BuiltinsBigIntTest, StringToBigInt)
{
    ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> bigint;
    JSHandle<EcmaString> str;
    JSHandle<JSTaggedValue> parma;

    // hex string
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0xffff"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::HEXADECIMAL);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("ffff"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0XFFFF"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::HEXADECIMAL);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("ffff"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    // binary string
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0b11111111"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::BINARY);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("11111111"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0B11111111"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::BINARY);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("11111111"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    // octal string
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0o123456"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::OCTAL);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("123456"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("0O123456"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint), BigInt::OCTAL);
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("123456"));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    // decimal string
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("999999999"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    str = BigInt::ToString(thread_, JSHandle<BigInt>::Cast(bigint));
    ASSERT_EQ(str->Compare(reinterpret_cast<EcmaString *>(parma->GetRawData())), 0);

    // string has space
    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("  123  "));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    JSHandle<JSTaggedValue> number(thread_, JSTaggedValue(static_cast<double>(123)));
    bool compareRes = JSTaggedValue::Equal(thread_, bigint, number);
    ASSERT_TRUE(compareRes);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("123   "));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(static_cast<double>(123)));
    compareRes = JSTaggedValue::Equal(thread_, bigint, number);
    ASSERT_TRUE(compareRes);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("   123"));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    number = JSHandle<JSTaggedValue>(thread_, JSTaggedValue(static_cast<double>(123)));
    compareRes = JSTaggedValue::Equal(thread_, bigint, number);
    ASSERT_TRUE(compareRes);

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString(""));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    ASSERT_TRUE(JSHandle<BigInt>::Cast(bigint)->IsZero());

    parma = JSHandle<JSTaggedValue>(factory->NewFromCanBeCompressString("    "));
    bigint = JSHandle<JSTaggedValue>(thread_, base::NumberHelper::StringToBigInt(thread_, parma));
    ASSERT_TRUE(bigint->IsBigInt());
    ASSERT_TRUE(JSHandle<BigInt>::Cast(bigint)->IsZero());
}

// NOLINTEND(readability-magic-numbers)

}  // namespace ark::test
